////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef __WN_CONTAINERS_RESOURCE_POINTER_H__
#define __WN_CONTAINERS_RESOURCE_POINTER_H__

#include "WNCore/inc/WNPlatform.h"
#include "WNCore/inc/WNTypes.h"
#include "WNCore/inc/WNAssert.h"
#include "WNCore/inc/WNAtomic.h"
#include "WNMemory/inc/WNAllocation.h"

namespace WNContainers {
    template <typename Type>
    class WNResourcePointer {
    public:
        WNResourcePointer() :
            mResource(WN_NULL) {
        }

        WNResourcePointer(Type* _resource) {
            WN_DEBUG_ASSERT(_resource != WN_NULL);
            WN_DEBUG_ASSERT(_resource->GetRefs() == 0);

            _resource->AddRef();

            mResource = _resource;
        }

        ~WNResourcePointer() {
            if (mResource != WN_NULL) {
                RemoveRef();
            }
        }

        WNResourcePointer(const WNResourcePointer<Type>& _resourcePointer) {
            _resourcePointer.mResource->AddRef();

            mResource = _resourcePointer.mResource;
        }

        WNResourcePointer<Type>& operator = (const WNResourcePointer<Type>& _resourcePointer) {
            _resourcePointer.mResource->AddRef();
            RemoveRef();

            mResource = _resourcePointer.mResource;

            return(*this);
        }

        Type* operator -> () {
            return(mResource);
        }

        const Type* operator -> () const {
            return(mResource);
        }

        Type& operator * () {
            return(*mResource);
        }

    private:
        WN_VOID RemoveRef() {
            if (mResource->RemoveRef()) {
                WN_DELETE(mResource);
            }

            mResource = WN_NULL;
        }

    private:
        Type* mResource; 
    };

    template <typename Type>
    WNResourcePointer<Type> WNAllocateResource() {
        return(WNResourcePointer<Type>(WN_NEW(Type)));
    }

    template <typename Type, typename Parameter>
    WNResourcePointer<Type> WNAllocateResource(Parameter& p) {
        return(WNResourcePointer<Type>(WN_NEW(Type(p))));
    }
}

#endif // __WN_CONTAINERS_RESOURCE_POINTER_H__